package com.example.learndemo.JVM.String;

/**
 * 主要是为了研究string内存地址问题，下面的测试方法最好每次只执行一个，因为涉及到常量池的问题，一次执行多个可能会对测试结果造成影响 String对象的创建很讲究，关键是要明白其原理：
 * 1：当使用任何方式来创建一个字符串对象s时，JVM会拿着这个s在常量池中找是否存在内容相同的字符串对象，如果不存在，则在池中创建一个字符串s，否则，不在池中添加。
 * 2：Java中，只要使用new关键字来创建对象，则一定会（在堆区或栈区）创建一个新的对象。
 * 3：使用直接指定或者使用纯字符串串联来创建String对象，则仅仅会检查维护String池中的字符串，池中没有就在池中创建一个，有则罢了！但绝不会在堆栈区再去创建该String对象。
 * 4：使用包含变量的表达式来创建String对象，则不仅会检查维护String池，而且还会在堆栈区创建一个String对象。最后指向堆内存中的对象
 */
public class StringExplain {
	private void test1() {// 热身运动，懒得解释
		String str1 = "ab";
		String str2 = "ab";
		String str3 = new String("ab");
		String str4 = new String("ab");
		String str5 = str4.intern();// 返回字符串对象对应的常量池中的值，如果常量池中有就直接返回，没有就创建再返回
		System.out.println(str1 == str2);// true
		System.out.println(str1 == str3);// false
		System.out.println(str3 == str4);// false
		System.out.println(str3 == str5);// false
		System.out.println(str1 == str5);// true
	}

	private void test2() {// 可以看class文件反编译后的结果
		String str1 = "a" + "b";
		String str2 = "ab";
		System.out.println(str1 == str2);// true String str1 = "a" + "b"会在编译后变成String str1 = "ab";所以返回true
	}

	private void test3() {
		String str1 = new String("a") + new String("b");// 这段代码执行后str1会指向堆中的字符串对象”ab“,但不会去扫描常量池
		String str2 = "ab";
		System.out.println(str1 == str2);// false
	}

	private void test4() {
		String str1 = new String("a") + new String("b");// 这段代码执行后str1会指向堆中的字符串对象”ab“,但不会去扫描常量池
		str1.intern();// 这段代码执行：先去常量池中找对应的“ab”,找不到会先在常量池中创建一个“ab”，并且把str1指向常量池中的“ab”
		String str2 = "ab";
		System.out.println(str1 == str2);// true
	}

	private void test5() {
		String str1 = new String("a") + new String("b");// 这段代码执行后str1会指向堆中的字符串对象”ab“,但不会去扫描常量池
		String str2 = "ab";// 这段代码执行会检查常量池中是否有“ab”，如果没有就创建；
		str1.intern();// 这段代码执行：先去常量池中找对应的“ab”,因为上面已经把“ab”放入常量池，所以就直接返回，str1指向没有变化还是指向堆中的对象
		System.out.println(str1 == str2);// false
	}

	private void test6() {// 可以看class文件反编译后的结果
		final String str1 = "a";// 用final修饰的常量池引用会在编译的时候直接替换掉这个局部变量出现的地方
		final String str2 = "b";
		String str3 = str1 + str2;// 由于上面变量用final修饰所以编译时会==》String str3 = "a" + "b";==>String str3 = "ab";
		String str4 = "ab";// 这段代码执行会检查常量池中是否有“ab”，如果没有就创建；
		System.out.println(str3 == str4);// ture
	}

	private void test7() {// 可以看class文件反编译后的结果
		final String str1 = new String("a");// 用final修饰的指向堆中的对象的变量，final会不起作用
		final String str2 = "b";
		String str3 = str1 + str2;// 由于上面变量用final修饰所以编译时会==》String str3 = str1 + "b";然后就和test3()一样了
		String str4 = "ab";// 这段代码执行会检查常量池中是否有“ab”，如果没有就创建；
		System.out.println(str3 == str4);// false
	}

	public static void main(String args[]) {
		StringExplain test = new StringExplain();
		// test.test1();
		// test.test2();
		// test.test2();
		// test.test3();
		// test.test4();
		// test.test5();
		// test.test6();
		test.test7();
	}


}
